namespace Angelus.Data.Linq.ProviderBase
{
	using System;
    using System.Collections.Generic;
    using System.Data.DLinq.Provider;
    using System.Data.DLinq.ProviderBase;
    using System.Expressions;
    using System.Reflection;

    internal class OracleFactory
    {
        private readonly TypeSystemProvider typeProvider;

        internal OracleFactory(TypeSystemProvider typeProvider)
        {
            this.typeProvider = typeProvider;
        }

        internal SqlExpression Add(params SqlExpression[] expressions)
        {
            SqlExpression right = expressions[expressions.Length - 1];
            for (int i = expressions.Length - 2; i >= 0; i--)
            {
                right = this.Binary(SqlNodeType.Add, expressions[i], right);
            }
            return right;
        }

        internal SqlExpression Add(SqlExpression expr, int second)
        {
            return this.Binary(SqlNodeType.Add, expr, this.Literal(second, expr.SourceExpression));
        }

        internal SqlExpression AndAccumulate(SqlExpression left, SqlExpression right)
        {
            if (left == null)
            {
                return right;
            }
            if (right == null)
            {
                return left;
            }
            return this.Binary(SqlNodeType.And, left, right);
        }

        internal SqlBinary Binary(SqlNodeType nodeType, SqlExpression left, SqlExpression right)
        {
            Type clrType = null;
            ProviderType sqlType = null;
            if (SqlNodeTypeOperators.IsPredicateBinaryOperator(nodeType))
            {
                clrType = typeof (bool);
                sqlType = this.typeProvider.GetApplicationType(0);
            }
            else
            {
                ProviderType type5 = this.typeProvider.PredictTypeForBinary(nodeType, left.SqlType, right.SqlType);
                if (type5 == right.SqlType)
                {
                    clrType = right.ClrType;
                    sqlType = right.SqlType;
                }
                else if (type5 == left.SqlType)
                {
                    clrType = left.ClrType;
                    sqlType = left.SqlType;
                }
                else
                {
                    sqlType = type5;
                    clrType = type5.GetClosestRuntimeType();
                }
            }
            return new SqlBinary(nodeType, clrType, sqlType, left, right);
        }

        internal SqlExpression BoolValue(bool value, Expression sourceExpression)
        {
            int boolValue = this.typeProvider.GetBoolValue(value);
            return this.Literal(boolValue, sourceExpression);
        }

        internal SqlExpression Case(Type clrType, SqlExpression discriminator, List<SqlExpression> matches,
                                    List<SqlExpression> values, Expression sourceExpression)
        {
            bool flag3 = true;
            bool flag4 = true;
            foreach (SqlExpression expression in values)
            {
                bool flag5 = SqlNodeTypeOperators.IsClientAidedExpression(expression);
                flag3 = flag3 && flag5;
                flag4 = flag4 && !flag5;
            }
            if (!flag3 && !flag4)
            {
                throw new Exception("Expression is neither all client aided nor all server aided.");
            }
            if (flag3 && flag4)
            {
                throw new Exception("Empty case is not supported.");
            }
            if (flag3)
            {
                List<SqlClientWhen> list = new List<SqlClientWhen>();
                int num = 0;
                int num2 = matches.Count;
                while (num < num2)
                {
                    list.Add(new SqlClientWhen(matches[num], values[num]));
                    num++;
                }
                return new SqlClientCase(clrType, discriminator, list, sourceExpression);
            }
            if (!flag4)
            {
                throw new Exception("Impossible.");
            }
            List<SqlWhen> whens = new List<SqlWhen>();
            int num3 = 0;
            int count = matches.Count;
            while (num3 < count)
            {
                whens.Add(new SqlWhen(matches[num3], values[num3]));
                num3++;
            }
            return new SqlSimpleCase(clrType, discriminator, whens, sourceExpression);
        }

        internal SqlExpression Concat(params SqlExpression[] expressions)
        {
            SqlExpression right = expressions[expressions.Length - 1];
            for (int i = expressions.Length - 2; i >= 0; i--)
            {
                right = this.Binary(SqlNodeType.Concat, expressions[i], right);
            }
            return right;
        }

        internal SqlExpression ConvertTo(Type clrType, SqlExpression expr)
        {
            return this.UnaryConvert(clrType, this.typeProvider.From(clrType), expr, expr.SourceExpression);
        }

        internal SqlExpression ConvertToBigint(SqlExpression expr)
        {
            return this.ConvertTo(typeof (long), expr);
        }

        internal SqlExpression ConvertToBool(SqlExpression expr)
        {
            return this.ConvertTo(typeof (bool), expr);
        }

        internal SqlExpression ConvertToDouble(SqlExpression expr)
        {
            return this.ConvertTo(typeof (double), expr);
        }

        internal SqlExpression ConvertToInt(SqlExpression expr)
        {
            return this.ConvertTo(typeof (int), expr);
        }

        internal ProviderType Default(MetaDataMember member)
        {
            if (member == null)
            {
                throw new ArgumentNullException("member");
            }
            if (member.DBType != null)
            {
                return this.typeProvider.Parse(member.DBType);
            }
            return this.typeProvider.From(member.MemberType);
        }

        internal ProviderType Default(Type clrType)
        {
            return this.typeProvider.From(clrType);
        }

        internal SqlExpression Divide(SqlExpression first, SqlExpression second)
        {
            return this.Binary(SqlNodeType.Div, first, second);
        }

        internal SqlExpression Divide(SqlExpression expr, long second)
        {
            return this.Binary(SqlNodeType.Div, expr, this.Literal(second, expr.SourceExpression));
        }

        internal SqlDoNotVisitExpression DoNotVisitExpression(SqlExpression expr)
        {
            return new SqlDoNotVisitExpression(expr);
        }

        internal SqlExprSet ExprSet(SqlExpression[] exprs, Expression sourceExpression)
        {
            return new SqlExprSet(exprs[0].ClrType, exprs[0].SqlType, exprs, sourceExpression);
        }

        internal SqlFunctionCall FunctionCall(Type clrType, string name, IEnumerable<SqlExpression> args,
                                              Expression source)
        {
            return new SqlFunctionCall(clrType, this.Default(clrType), name, args, source);
        }

        internal SqlIncluding Including(SqlExpression expr, Expression sourceExpression)
        {
            return new SqlIncluding(expr, sourceExpression);
        }

        internal SqlExpression LEN(SqlExpression expr)
        {
            return this.FunctionCall(typeof (int), "LEN", new SqlExpression[] {expr}, expr.SourceExpression);
        }

        internal SqlLike Like(SqlExpression expr, SqlExpression pattern, SqlExpression escape, Expression source)
        {
            return new SqlLike(typeof (bool), this.typeProvider.GetApplicationType(0), expr, pattern, escape, source);
        }

        internal SqlLiteral Literal(object value, Expression sourceExpression)
        {
            Type clrType;
            ProviderType sqlType;
            if (value != null)
            {
                clrType = value.GetType();
            }
            else
            {
                clrType = typeof (object);
            }
            if (value is bool)
            {
                sqlType = this.typeProvider.From(typeof (int));
            }
            else
            {
                sqlType = this.Default(clrType);
            }
            return new SqlLiteral(clrType, sqlType, value, sourceExpression);
        }

        internal SqlMember Member(SqlExpression expr, MetaDataMember member)
        {
            return new SqlMember(member.MemberType, this.Default(member), expr, member.Member);
        }

        internal SqlMember Member(SqlExpression expr, MemberInfo member)
        {
            Type memberType = TypeSystem.GetMemberType(member);
            return new SqlMember(memberType, this.Default(memberType), expr, member);
        }

        internal SqlMethodCall MethodCall(MethodInfo method, SqlExpression obj, SqlExpression[] args,
                                          Expression sourceExpression)
        {
            return new SqlMethodCall(method.ReturnType, this.Default(method.ReturnType), method, obj, args,
                                     sourceExpression);
        }

        internal SqlExpression Mod(SqlExpression expr, long second)
        {
            return this.Binary(SqlNodeType.Mod, expr, this.Literal(second, expr.SourceExpression));
        }

        internal SqlExpression Multiply(params SqlExpression[] expressions)
        {
            SqlExpression right = expressions[expressions.Length - 1];
            for (int i = expressions.Length - 2; i >= 0; i--)
            {
                right = this.Binary(SqlNodeType.Mul, expressions[i], right);
            }
            return right;
        }

        internal SqlExpression Multiply(SqlExpression expr, long second)
        {
            return this.Binary(SqlNodeType.Mul, expr, this.Literal(second, expr.SourceExpression));
        }

        internal SqlNew New(MetaType type, ConstructorInfo cons, IEnumerable<SqlExpression> args,
                            IEnumerable<SqlMemberAssign> bindings, Expression sourceExpression)
        {
            return new SqlNew(type, this.typeProvider.From(type.Type), cons, args, bindings, sourceExpression);
        }

        internal SqlOptionalValue OptionalValue(SqlExpression hasValue, SqlExpression value)
        {
            return new SqlOptionalValue(hasValue, value);
        }

        internal SqlExpression OrAccumulate(SqlExpression left, SqlExpression right)
        {
            if (left == null)
            {
                return right;
            }
            if (right == null)
            {
                return left;
            }
            return this.Binary(SqlNodeType.Or, left, right);
        }

        internal SqlExpression Parameter(object value, Expression source)
        {
            Type type = value.GetType();
            SqlClientParameter expression = new SqlClientParameter(type, this.typeProvider.From(type), value, source);
            return this.Unary(SqlNodeType.ClientExpression, expression, source);
        }

        internal SqlSearchedCase SearchedCase(SqlWhen[] whens, SqlExpression @else, Expression sourceExpression)
        {
            return new SqlSearchedCase(whens[0].Value.ClrType, whens, @else, sourceExpression);
        }

        internal SqlSubSelect SubSelect(SqlNodeType nt, SqlSelect select)
        {
            Type clrType = select.Selection.ClrType;
            ProviderType sqlType = select.Selection.SqlType;
            SqlNodeType type5 = nt;
            if (type5 <= SqlNodeType.Exists)
            {
                if ((type5 != SqlNodeType.Element) && (type5 == SqlNodeType.Exists))
                {
                    clrType = typeof (bool);
                    sqlType = this.typeProvider.GetApplicationType(0);
                }
            }
            else
            {
                switch (type5)
                {
                    case SqlNodeType.Multiset:
                        clrType = typeof (IEnumerable<>).MakeGenericType(new Type[] {clrType});
                        break;
                }
            }
            return new SqlSubSelect(nt, clrType, sqlType, select);
        }

        internal SqlExpression Subtract(SqlExpression first, SqlExpression second)
        {
            return this.Binary(SqlNodeType.Sub, first, second);
        }

        internal SqlExpression Subtract(SqlExpression expr, int second)
        {
            return this.Binary(SqlNodeType.Sub, expr, this.Literal(second, expr.SourceExpression));
        }

        internal SqlTable Table(MetaTable table, MetaType rowType, Expression sourceExpression)
        {
            return new SqlTable(table, rowType, this.typeProvider.GetApplicationType(1), sourceExpression);
        }

        internal SqlTableValuedFunctionCall TableValuedFunctionCall(MetaType rowType, Type clrType, string name,
                                                                    IEnumerable<SqlExpression> args, Expression source)
        {
            return new SqlTableValuedFunctionCall(rowType, clrType, this.Default(clrType), name, args, source);
        }

        internal SqlExpression TypeCase(Type clrType, MetaType rowType, SqlExpression discriminator,
                                        IEnumerable<SqlTypeCaseWhen> whens, Expression sourceExpression)
        {
            return new SqlTypeCase(clrType, this.typeProvider.From(clrType), rowType, discriminator, whens,
                                   sourceExpression);
        }

        public SqlLiteral TypedLiteralNull(Type type, Expression sourceExpression)
        {
            SqlLiteral literal2 = this.Literal(null, sourceExpression);
            literal2.SetClrType(type);
            return literal2;
        }

        internal SqlUnary Unary(SqlNodeType nodeType, SqlExpression expression, Expression sourceExpression)
        {
            Type clrType;
            ProviderType sqlType;
            if (nodeType == SqlNodeType.Count)
            {
                clrType = typeof (int);
                sqlType = this.typeProvider.From(typeof (decimal));
            }
            else if (nodeType == SqlNodeType.LongCount)
            {
                clrType = typeof (long);
                sqlType = this.typeProvider.From(typeof (long));
            }
            else if (SqlNodeTypeOperators.IsPredicateUnaryOperator(nodeType))
            {
                clrType = typeof (bool);
                sqlType = this.typeProvider.GetApplicationType(0);
            }
            else
            {
                clrType = expression.ClrType;
                sqlType = expression.SqlType;
            }
            return new SqlUnary(nodeType, clrType, sqlType, expression, sourceExpression);
        }

        internal SqlUnary UnaryConvert(Type targetClrType, ProviderType targetSqlType, SqlExpression expression,
                                       Expression sourceExpression)
        {
            return new SqlUnary(SqlNodeType.Convert, targetClrType, targetSqlType, expression, sourceExpression);
        }

        internal SqlUnary UnaryValueOf(SqlExpression expression, Expression sourceExpression)
        {
            return new SqlUnary(SqlNodeType.ValueOf, TypeSystem.GetNonNullableType(expression.ClrType),
                                expression.SqlType, expression, sourceExpression);
        }
    }
}